﻿using SQLBuilder.Extensions;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace K3Cloud.WebApi.Core.IoC.Extensions
{
    /// <summary>
    /// 逻辑
    /// </summary>
    public enum EnumAndOr : int
    {
        /// <summary>
        /// 并且
        /// </summary>
        And,
        /// <summary>
        /// 或者
        /// </summary>
        Or
    }
    /// <summary>
    /// LinqExtensions 扩展表达式树操作，简化筛选条件的动态拼装。
    /// 支持条件追加、逻辑组合等常用操作。
    /// </summary>
    public static class LinqExtensions
    {
        /// <summary>
        /// 判断类型是否为数字类型
        /// </summary>
        private static bool IsNumericType(Type type)
        {
            return type == typeof(int) || 
                   type == typeof(short) || 
                   type == typeof(long) || 
                   type == typeof(float) || 
                   type == typeof(double) || 
                   type == typeof(decimal) || 
                   type == typeof(byte) ||
                   type == typeof(sbyte) ||
                   type == typeof(uint) ||
                   type == typeof(ushort) ||
                   type == typeof(ulong);
        }

        /// <summary>
        /// 获取类型的默认值
        /// </summary>
        private static object GetDefaultValue(Type type)
        {
            return type.IsValueType ? Activator.CreateInstance(type)! : null!;
        }

        /// <summary>
        /// 增加筛选条件到表达式树
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="thisExpression">基树</param>
        /// <param name="fieldName">字段</param>
        /// <param name="optionString">比较</param>
        /// <param name="value">值</param>
        /// <param name="expressionType">逻辑</param>
        /// <returns></returns>
        public static Expression<Func<T, bool>> AddFilter<T>(this Expression<Func<T, bool>>? thisExpression, string? fieldName, string? optionString, object? value, EnumAndOr expressionType = default)
        {
            Type t = typeof(T);
            PropertyInfo propertyAny = t.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(x => x.Name.Equals(fieldName, StringComparison.OrdinalIgnoreCase)).FirstOrDefault() ?? throw new K3ApiException($"类型 {t.FullName} 中不存在属性 '{fieldName}'，请检查属性名称是否正确（不区分大小写）!");
            ParameterExpression parameterExpression = Expression.Parameter(t);
            PropertyInfo property = propertyAny;
            Type propertyType = property.PropertyType;
            Type underlyingType = propertyType.ParseNullableToType();
            
            // 增强：更完整的数字类型判断
            object? propertyValue = IsNumericType(underlyingType)
                ? value != null ? Convert.ChangeType(value, underlyingType) : GetDefaultValue(underlyingType)
                : value != null ? Convert.ChangeType(value, underlyingType) : null; 

            MemberExpression expressionProperty = Expression.PropertyOrField(parameterExpression, property.Name);
            ConstantExpression expressionValue = Expression.Constant(propertyValue, propertyType);
            Expression[] paramsValue = [expressionValue];
            Type[] paramsType = [propertyType];
            MethodInfo? optionField_Contains = propertyType.GetMethod("Contains", paramsType);
            MethodInfo? optionField_IsNullOrEmpty = propertyType.GetMethod("IsNullOrEmpty", paramsType);
            MethodInfo? optionField_StartsWith = propertyType.GetMethod("StartsWith", paramsType);
            MethodInfo? optionField_EndsWith = propertyType.GetMethod("EndsWith", paramsType);

            Expression optionExpression = Expression.Empty();
            switch (optionString)
            {
                case "=":
                case "equal":
                    optionExpression = Expression.Equal(expressionProperty, expressionValue);
                    break;
                case "!=":
                case "<>":
                case "unequal":
                    optionExpression = Expression.NotEqual(expressionProperty, expressionValue);
                    break;
                case ">":
                case "greater_than":
                    optionExpression = Expression.GreaterThan(expressionProperty, expressionValue);
                    break;
                case ">=":
                    optionExpression = Expression.GreaterThanOrEqual(expressionProperty, expressionValue);
                    break;
                case "<":
                case "less_than":
                    optionExpression = Expression.LessThan(expressionProperty, expressionValue);
                    break;
                case "<=":
                    optionExpression = Expression.LessThanOrEqual(expressionProperty, expressionValue);
                    break;
                case "like":
                    if (optionField_Contains != null)
                    {
                        optionExpression = Expression.Call(expressionProperty, optionField_Contains, paramsValue);
                    }
                    break;
                case "notlike":
                    if (optionField_Contains != null)
                    {
                        optionExpression = Expression.Not(Expression.Call(expressionProperty, optionField_Contains, paramsValue));
                    }
                    break;
                case "llike":
                case "start":
                    if (optionField_StartsWith != null)
                    {
                        optionExpression = Expression.Call(expressionProperty, optionField_StartsWith, paramsValue);
                    }
                    break;
                case "rlike":
                case "end":
                    if (optionField_EndsWith != null)
                    {
                        optionExpression = Expression.Call(expressionProperty, optionField_EndsWith, paramsValue);
                    }
                    break;
                case "empty":
                    if (optionField_IsNullOrEmpty != null)
                    {
                        optionExpression = Expression.Call(null, optionField_IsNullOrEmpty, expressionProperty);
                    }
                    break;
                case "notempty":
                    if (optionField_IsNullOrEmpty != null)
                    {
                        optionExpression = Expression.Not(Expression.Call(null, optionField_IsNullOrEmpty, expressionProperty));
                    }
                    break;
                default:
                    break;
            }

            Expression<Func<T, bool>> expression = Expression.Lambda<Func<T, bool>>(optionExpression, [parameterExpression]);
            return thisExpression == null
                ? expression
                : expressionType == EnumAndOr.And ? thisExpression.And(expression) : thisExpression.Or(expression);
        }

        /// <summary>
        /// 添加And条件
        /// <![CDATA[
        /// 例：Expression<Func<PRD_INSTOCK, bool>> t = default;
        /// t = t.AndFilter(x => x.FBillNo != null);
        /// ]]>
        /// </summary>
        [DebuggerStepThrough]
        public static Expression<Func<T, bool>> AndFilter<T>(this Expression<Func<T, bool>>? first, Expression<Func<T, bool>> second)
        {
            return first == null ? second : first.And(second);
        }

        /// <summary>
        /// 添加Or条件
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="first"></param>
        /// <param name="second"></param>
        /// <returns></returns>
        [DebuggerStepThrough]
        public static Expression<Func<T, bool>> OrFilter<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
        {
            return first == null ? second : first.Or(second);
        }

        /// <summary>
        /// 添加In筛选条件（字段值在集合中）
        /// 例：t.AddInFilter("FStatus", new[] { "A", "B", "C" })
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <typeparam name="TValue">字段值类型</typeparam>
        /// <param name="thisExpression">基础表达式</param>
        /// <param name="fieldName">字段名</param>
        /// <param name="values">值集合</param>
        /// <param name="expressionType">逻辑运算符</param>
        /// <returns>组合后的表达式</returns>
        [DebuggerStepThrough]
        public static Expression<Func<T, bool>> AddInFilter<T, TValue>(
            this Expression<Func<T, bool>>? thisExpression, 
            string fieldName, 
            IEnumerable<TValue> values, 
            EnumAndOr expressionType = default)
        {
            if (values == null || !values.Any())
            {
                throw new ArgumentException("In查询的值集合不能为空", nameof(values));
            }

            Type t = typeof(T);
            PropertyInfo propertyAny = t.GetProperties(BindingFlags.Public | BindingFlags.Instance)
                .Where(x => x.Name.Equals(fieldName, StringComparison.OrdinalIgnoreCase))
                .FirstOrDefault() ?? throw new K3ApiException($"类型 {t.FullName} 中不存在属性 '{fieldName}'，请检查属性名称是否正确（不区分大小写）!");

            ParameterExpression parameterExpression = Expression.Parameter(t);
            MemberExpression expressionProperty = Expression.PropertyOrField(parameterExpression, propertyAny.Name);

            // 创建包含检查：values.Contains(entity.Property)
            ConstantExpression valuesConstant = Expression.Constant(values);
            MethodInfo containsMethod = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public)
                .Single(m => m.Name == nameof(Enumerable.Contains) && m.GetParameters().Length == 2)
                .MakeGenericMethod(propertyAny.PropertyType);
            
            MethodCallExpression containsExpression = Expression.Call(null, containsMethod, valuesConstant, expressionProperty);
            Expression<Func<T, bool>> expression = Expression.Lambda<Func<T, bool>>(containsExpression, parameterExpression);

            return thisExpression == null
                ? expression
                : expressionType == EnumAndOr.And ? thisExpression.And(expression) : thisExpression.Or(expression);
        }

        /// <summary>
        /// 添加NotIn筛选条件（字段值不在集合中）
        /// 例：t.AddNotInFilter("FStatus", new[] { "X", "Y", "Z" })
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <typeparam name="TValue">字段值类型</typeparam>
        /// <param name="thisExpression">基础表达式</param>
        /// <param name="fieldName">字段名</param>
        /// <param name="values">值集合</param>
        /// <param name="expressionType">逻辑运算符</param>
        /// <returns>组合后的表达式</returns>
        [DebuggerStepThrough]
        public static Expression<Func<T, bool>> AddNotInFilter<T, TValue>(
            this Expression<Func<T, bool>>? thisExpression, 
            string fieldName, 
            IEnumerable<TValue> values, 
            EnumAndOr expressionType = default)
        {
            if (values == null || !values.Any())
            {
                throw new ArgumentException("NotIn查询的值集合不能为空", nameof(values));
            }

            Type t = typeof(T);
            PropertyInfo propertyAny = t.GetProperties(BindingFlags.Public | BindingFlags.Instance)
                .Where(x => x.Name.Equals(fieldName, StringComparison.OrdinalIgnoreCase))
                .FirstOrDefault() ?? throw new K3ApiException($"类型 {t.FullName} 中不存在属性 '{fieldName}'，请检查属性名称是否正确（不区分大小写）!");

            ParameterExpression parameterExpression = Expression.Parameter(t);
            MemberExpression expressionProperty = Expression.PropertyOrField(parameterExpression, propertyAny.Name);

            // 创建不包含检查：!values.Contains(entity.Property)
            ConstantExpression valuesConstant = Expression.Constant(values);
            MethodInfo containsMethod = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public)
                .Single(m => m.Name == nameof(Enumerable.Contains) && m.GetParameters().Length == 2)
                .MakeGenericMethod(propertyAny.PropertyType);
            
            MethodCallExpression containsExpression = Expression.Call(null, containsMethod, valuesConstant, expressionProperty);
            UnaryExpression notContainsExpression = Expression.Not(containsExpression);
            Expression<Func<T, bool>> expression = Expression.Lambda<Func<T, bool>>(notContainsExpression, parameterExpression);

            return thisExpression == null
                ? expression
                : expressionType == EnumAndOr.And ? thisExpression.And(expression) : thisExpression.Or(expression);
        }

        /// <summary>
        /// 添加Between范围筛选条件（字段值在最小值和最大值之间）
        /// 例：t.AddBetweenFilter("FAge", 18, 60)
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="thisExpression">基础表达式</param>
        /// <param name="fieldName">字段名</param>
        /// <param name="minValue">最小值（包含）</param>
        /// <param name="maxValue">最大值（包含）</param>
        /// <param name="expressionType">逻辑运算符</param>
        /// <returns>组合后的表达式</returns>
        [DebuggerStepThrough]
        public static Expression<Func<T, bool>> AddBetweenFilter<T>(
            this Expression<Func<T, bool>>? thisExpression, 
            string fieldName, 
            object? minValue, 
            object? maxValue, 
            EnumAndOr expressionType = default)
        {
            // 添加 >= minValue 条件
            var expression = thisExpression.AddFilter(fieldName, ">=", minValue, expressionType);
            
            // 添加 <= maxValue 条件
            expression = expression.AddFilter(fieldName, "<=", maxValue, EnumAndOr.And);
            
            return expression;
        }

    }
}
